/* -------------------------------- tools.js -------------------------------- */
(function() {
   var functions = [],
      cssVendorPrefix =
         VED.env.gecko ? '-moz-' :
         VED.env.webkit ? '-webkit-' :
         VED.env.opera ? '-o-' :
         VED.env.ie ? '-ms-' :
         '';

   VED.on( 'reset', function() {
      functions = [];
   });

   VED.tools = {

      arrayCompare: function( arrayA, arrayB ) {
         if ( !arrayA && !arrayB )
            return true;

         if ( !arrayA || !arrayB || arrayA.length != arrayB.length )
            return false;

         for ( var i = 0; i < arrayA.length; i++ ) {
            if ( arrayA[ i ] != arrayB[ i ] )
               return false;
         }

         return true;
      },

      clone: function( obj ) {
         var clone;

         // Array.
         if ( obj && ( obj instanceof Array ) ) {
            clone = [];

            for ( var i = 0; i < obj.length; i++ )
               clone[ i ] = VED.tools.clone( obj[ i ] );

            return clone;
         }

         // "Static" types.
         if ( obj === null || ( typeof( obj ) != 'object' ) || ( obj instanceof String ) || ( obj instanceof Number ) || ( obj instanceof Boolean ) || ( obj instanceof Date ) || ( obj instanceof RegExp ) ) {
            return obj;
         }

         // Objects.
         clone = new obj.constructor();

         for ( var propertyName in obj ) {
            var property = obj[ propertyName ];
            clone[ propertyName ] = VED.tools.clone( property );
         }

         return clone;
      },

      capitalize: function( str ) {
         return str.charAt( 0 ).toUpperCase() + str.substring( 1 ).toLowerCase();
      },

      extend: function( target ) {
         var argsLength = arguments.length,
            overwrite, propertiesList;

         if ( typeof( overwrite = arguments[ argsLength - 1 ] ) == 'boolean' )
            argsLength--;
         else if ( typeof( overwrite = arguments[ argsLength - 2 ] ) == 'boolean' ) {
            propertiesList = arguments[ argsLength - 1 ];
            argsLength -= 2;
         }
         for ( var i = 1; i < argsLength; i++ ) {
            var source = arguments[ i ];
            for ( var propertyName in source ) {
               // Only copy existed fields if in overwrite mode.
               if ( overwrite === true || target[ propertyName ] == undefined ) {
                  // Only copy  specified fields if list is provided.
                  if ( !propertiesList || ( propertyName in propertiesList ) )
                     target[ propertyName ] = source[ propertyName ];

               }
            }
         }

         return target;
      },

      prototypedCopy: function( source ) {
         var copy = function() {};
         copy.prototype = source;
         return new copy();
      },

      copy: function( source ) {
         var obj = {},
            name;

         for ( name in source )
            obj[ name ] = source[ name ];

         return obj;
      },

      isArray: function( object ) {
         return ( !!object && object instanceof Array );
      },

      isEmpty: function( object ) {
         for ( var i in object ) {
            if ( object.hasOwnProperty( i ) )
               return false;
         }
         return true;
      },

      cssVendorPrefix: function( property, value, asString ) {
         if ( asString )
            return cssVendorPrefix + property + ':' + value + ';' + property + ':' + value;

         var ret = {};
         ret[ property ] = value;
         ret[ cssVendorPrefix + property ] = value;

         return ret;
      },

      cssStyleToDomStyle: (function() {
         var test = document.createElement( 'div' ).style;

         var cssFloat = ( typeof test.cssFloat != 'undefined' ) ? 'cssFloat' : ( typeof test.styleFloat != 'undefined' ) ? 'styleFloat' : 'float';

         return function( cssName ) {
            if ( cssName == 'float' )
               return cssFloat;
            else {
               return cssName.replace( /-./g, function( match ) {
                  return match.substr( 1 ).toUpperCase();
               });
            }
         };
      })(),

      buildStyleHtml: function( css ) {
         css = [].concat( css );
         var item,
            retval = [];
         for ( var i = 0; i < css.length; i++ ) {
            if ( ( item = css[ i ] ) ) {
               // Is CSS style text ?
               if ( /@import|[{}]/.test( item ) )
                  retval.push( '<style>' + item + '</style>' );
               else
                  retval.push( '<link type="text/css" rel=stylesheet href="' + item + '">' );
            }
         }
         return retval.join( '' );
      },

      htmlEncode: function( text ) {
         return String( text ).replace( /&/g, '&amp;' ).replace( />/g, '&gt;' ).replace( /</g, '&lt;' );
      },

      htmlEncodeAttr: function( text ) {
         return text.replace( /"/g, '&quot;' ).replace( /</g, '&lt;' ).replace( />/g, '&gt;' );
      },

      getNextNumber: (function() {
         var last = 0;
         return function() {
            return ++last;
         };
      })(),

      getNextId: function() {
         return 'ved_' + this.getNextNumber();
      },

      override: function( originalFunction, functionBuilder ) {
         var newFn = functionBuilder( originalFunction );
         newFn.prototype = originalFunction.prototype;
         return newFn;
      },

      setTimeout: function( func, milliseconds, scope, args, ownerWindow ) {
         if ( !ownerWindow )
            ownerWindow = window;

         if ( !scope )
            scope = ownerWindow;

         return ownerWindow.setTimeout( function() {
            if ( args )
               func.apply( scope, [].concat( args ) );
            else
               func.apply( scope );
         }, milliseconds || 0 );
      },
/*
      trim: (function() {
         // We are not using \s because we don't want "non-breaking spaces" to be caught.
         var trimRegex = /(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g;
         return function( str ) {
            return str.replace( trimRegex, '' );
         };
      })(),

      ltrim: (function() {
         // We are not using \s because we don't want "non-breaking spaces" to be caught.
         var trimRegex = /^[ \t\n\r]+/g;
         return function( str ) {
            return str.replace( trimRegex, '' );
         };
      })(),

      rtrim: (function() {
         // We are not using \s because we don't want "non-breaking spaces" to be caught.
         var trimRegex = /[ \t\n\r]+$/g;
         return function( str ) {
            return str.replace( trimRegex, '' );
         };
      })(),
*/
      indexOf: function( array, value ) {
         if ( typeof value == 'function' ) {
            for ( var i = 0, len = array.length; i < len; i++ ) {
               if ( value( array[ i ] ) )
                  return i;
            }
         } else if ( array.indexOf ) {
            return array.indexOf( value );
         } else {
            for ( i = 0, len = array.length; i < len; i++ ) {
               if ( array[ i ] === value )
                  return i;
            }
         }
         return -1;
      },

      search: function( array, value ) {
         var index = VED.tools.indexOf( array, value );
         return index >= 0 ? array[ index ] : null;
      },

      bind: function( func, obj ) {
         return function() {
            return func.apply( obj, arguments );
         };
      },
/*
      createClass: function( definition ) {
         var $ = definition.$,
            baseClass = definition.base,
            privates = definition.privates || definition._,
            proto = definition.proto,
            statics = definition.statics;

         // Create the constructor, if not present in the definition.
         !$ && ( $ = function() {
            baseClass && this.base.apply( this, arguments );
         });

         if ( privates ) {
            var originalConstructor = $;
            $ = function() {
               // Create (and get) the private namespace.
               var _ = this._ || ( this._ = {} );

               // Make some magic so "this" will refer to the main
               // instance when coding private functions.
               for ( var privateName in privates ) {
                  var priv = privates[ privateName ];

                  _[ privateName ] = ( typeof priv == 'function' ) ? VED.tools.bind( priv, this ) : priv;
               }

               originalConstructor.apply( this, arguments );
            };
         }

         if ( baseClass ) {
            $.prototype = this.prototypedCopy( baseClass.prototype );
            $.prototype.constructor = $;
            // Super references.
            $.base = baseClass;
            $.baseProto = baseClass.prototype;
            // Super constructor.
            $.prototype.base = function() {
               this.base = baseClass.prototype.base;
               baseClass.apply( this, arguments );
               this.base = arguments.callee;
            };
         }

         if ( proto )
            this.extend( $.prototype, proto, true );

         if ( statics )
            this.extend( $, statics, true );

         return $;
      },
*/
      addFunction: function( fn, scope ) {
         return functions.push( function() {
            return fn.apply( scope || this, arguments );
         }) - 1;
      },

      removeFunction: function( ref ) {
         functions[ ref ] = null;
      },

      callFunction: function( ref ) {
         var fn = functions[ ref ];
         return fn && fn.apply( window, Array.prototype.slice.call( arguments, 1 ) );
      },

      cssLength: (function() {
         var pixelRegex = /^-?\d+\.?\d*px$/,
            lengthTrimmed;

         return function( length ) {
            lengthTrimmed = ( length + '' ).trim() + 'px';

            if ( pixelRegex.test( lengthTrimmed ) )
               return lengthTrimmed;
            else
               return length || '';
         };
      })(),

      convertToPx: (function() {
         var calculator;

         return function( cssLength ) {
            if ( !calculator ) {
               calculator = VED.dom.element.createFromHtml( '<div style="position:absolute;left:-9999px;' +
                  'top:-9999px;margin:0px;padding:0px;border:0px;"' +
                  '></div>', VED.document );
               VED.document.getBody().append( calculator );
            }

            if ( !( /%$/ ).test( cssLength ) ) {
               calculator.setStyle( 'width', cssLength );
               return calculator.$.clientWidth;
            }

            return cssLength;
         };
      })(),

      repeat: function( str, times ) {
         return new Array( times + 1 ).join( str );
      },

      tryThese: function() {
         var returnValue;
         for ( var i = 0, length = arguments.length; i < length; i++ ) {
            var lambda = arguments[ i ];
            try {
               returnValue = lambda();
               break;
            } catch ( e ) {}
         }
         return returnValue;
      },

      genKey: function() {
         return Array.prototype.slice.call( arguments ).join( '-' );
      },

      defer: function( fn ) {
         return function() {
            var args = arguments,
               self = this;
            window.setTimeout( function() {
               fn.apply( self, args );
            }, 0 );
         };
      },

      normalizeCssText: function( styleText, nativeNormalize ) {
         var props = [],
            name,
            parsedProps = VED.tools.parseCssText( styleText, true, nativeNormalize );

         for ( name in parsedProps )
            props.push( name + ':' + parsedProps[ name ] );

         props.sort();

         return props.length ? ( props.join( ';' ) + ';' ) : '';
      },

      convertRgbToHex: function( styleText ) {
         return styleText.replace( /(?:rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\))/gi, function( match, red, green, blue ) {
            var color = [ red, green, blue ];
            // Add padding zeros if the hex value is less than 0x10.
            for ( var i = 0; i < 3; i++ )
               color[ i ] = ( '0' + parseInt( color[ i ], 10 ).toString( 16 ) ).slice( -2 );
            return '#' + color.join( '' );
         });
      },

      parseCssText: function( styleText, normalize, nativeNormalize ) {
         var retval = {};

         if ( nativeNormalize ) {
            // Injects the style in a temporary span object, so the browser parses it,
            // retrieving its final format.
            var temp = new VED.dom.element( 'span' );
            temp.setAttribute( 'style', styleText );
            styleText = VED.tools.convertRgbToHex( temp.getAttribute( 'style' ) || '' );
         }

         // IE will leave a single semicolon when failed to parse the style text. (#3891)
         if ( !styleText || styleText == ';' )
            return retval;

         styleText.replace( /&quot;/g, '"' ).replace( /\s*([^:;\s]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value ) {
            if ( normalize ) {
               name = name.toLowerCase();
               // Normalize font-family property, ignore quotes and being case insensitive. (#7322)
               // http://www.w3.org/TR/css3-fonts/#font-family-the-font-family-property
               if ( name == 'font-family' )
                  value = value.toLowerCase().replace( /["']/g, '' ).replace( /\s*,\s*/g, ',' );
               value = value.trim();
            }

            retval[ name ] = value;
         });
         return retval;
      },
      
      writeCssText: function( styles, sort ) {
			var name,
				stylesArr = [];

			for ( name in styles )
				stylesArr.push( name + ':' + styles[ name ] );

			if ( sort )
				stylesArr.sort();

			return stylesArr.join( '; ' );
		},

		objectCompare: function( left, right, onlyLeft ) {
			var name;

			if ( !left && !right )
				return true;
			if ( !left || !right )
				return false;

			for ( name in left ) {
				if ( left[ name ] != right[ name ] ) {
					return false;
				}
			}

			if ( !onlyLeft ) {
				for ( name in right ) {
					if ( left[ name ] != right[ name ] )
						return false;
				}
			}

			return true;
		},

		objectKeys: function( obj ) {
			var keys = [];
			for ( var i in obj )
				keys.push( i );

			return keys;
		},

		convertArrayToObject: function( arr, fillWith ) {
			var obj = {};

			if ( arguments.length == 1 )
				fillWith = true;

			for ( var i = 0, l = arr.length; i < l; ++i )
				obj[ arr[ i ] ] = fillWith;

			return obj;
		},

		fixDomain: function() {
			var domain;

			while( 1 ) {
				try {
					// Try to access the parent document. It throws
					// "access denied" if restricted by the "Same Origin" policy.
					domain = window.parent.document.domain;
					break;
				} catch ( e ) {
					// Calculate the value to set to document.domain.
					domain = domain ?

						// If it is not the first pass, strip one part of the
						// name. E.g.  "test.example.com"  => "example.com"
						domain.replace( /.+?(?:\.|$)/, '' ) :

						// In the first pass, we'll handle the
						// "document.domain = document.domain" case.
						document.domain;

					// Stop here if there is no more domain parts available.
					if ( !domain )
						break;

					document.domain = domain;
				}
			}

			return !!domain;
		}
   };
})();





